package handler

import (
	"errors"
	"fmt"
	"log"
	"moft/database"
	"moft/model"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type ClientHandler struct {
	db *gorm.DB
}

func NewClientHandler() *ClientHandler {
	return &ClientHandler{
		db: database.DB(),
	}
}

type clientRequestBody struct {
	Name    string `json:"name"`
	Phone   string `json:"phone"`
	Email   string `json:"email"`
	Address string `json:"address"`
	Company string `json:"company"`
	Remarks string `json:"remarks"`
	Enabled bool   `json:"enabled"`
}

func (c *clientRequestBody) toClient() model.Client {
	return model.Client{
		Name:    c.Name,
		Phone:   c.Phone,
		Email:   c.Email,
		Address: c.Address,
		Company: c.Company,
		Remarks: c.Remarks,
	}
}

func (h *ClientHandler) Create(c *gin.Context) {
	var payload struct {
		Client clientRequestBody `json:"client" binding:"required"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "the request body is error",
			"message": err.Error(),
		})
		return
	}
	if payload.Client.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the client name is empty",
		})
		return
	}

	_, exist, err := h.checkDuplicateClient(payload.Client.Name)
	if err != nil {
		log.Printf("check duplicate client failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}
	if exist {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the client is exist, please retype",
		})
		return
	}

	client := payload.Client.toClient()
	client.Enabled = true
	err = model.CreateClient(h.db, &client)
	if err != nil {
		log.Printf("create client failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}

	cli, err := model.FindClientByName(h.db, client.Name)
	if err != nil {
		log.Printf("find client by name %s failed: %v", client.Name, err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"client": cli,
	})
}

func (h *ClientHandler) checkDuplicateClient(name string) (*model.Client, bool, error) {
	c, err := model.FindClientByName(h.db, name)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, false, nil
	} else if err != nil {
		return nil, false, err
	}

	return c, true, nil
}

func (h *ClientHandler) Update(c *gin.Context) {
	var payload struct {
		Client clientRequestBody `json:"client" binding:"required"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}
	cli, err := model.FindClientByName(h.db, payload.Client.Name)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": fmt.Sprintf("the client %s is not exist", payload.Client.Name),
		})
		return
	}

	client := payload.Client.toClient()
	client.ID = cli.ID
	if err := model.UpdateClient(h.db, &client); err != nil {
		log.Printf("update client failed: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": err.Error(),
		})
		return
	}

	c.Status(http.StatusCreated)
}

func (h *ClientHandler) Enabled(c *gin.Context) {
	var payload struct {
		Enabled bool `json:"enabled" binding:"required"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	name := c.Param("client_name")
	client, err := model.FindClientByName(h.db, name)
	if err != nil {
		log.Printf("find client by name %s failed: %v", name, err)
		c.Status(http.StatusInternalServerError)
		return
	}

	client.Enabled = payload.Enabled
	if err := model.UpdateClient(h.db, client); err != nil {
		log.Printf("update client failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.Status(http.StatusCreated)
}

func (h *ClientHandler) List(c *gin.Context) {
	clients, err := model.ListClient(h.db, nil)
	if err != nil {
		log.Printf("list clients failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"clients": clients,
	})
}

func (h *ClientHandler) FindByName(c *gin.Context) {
	clientName := c.Param("client_name")

	client, err := model.FindClientByName(h.db, clientName)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "resource not found",
		})
		return
	} else if err != nil {
		log.Printf("find client by name %s failed: %v", clientName, err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"client": client,
	})
}
